package com.bc.web.controller.merch;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import com.alibaba.fastjson.JSONObject;
import com.bc.common.CommonEnum.OrderStatus;
import com.bc.common.CommonEnum.Resp;
import com.bc.common.PayType;
import com.bc.common.message.BaseResp;
import com.bc.common.message.DFReq;
import com.bc.web.config.SystemConfig;
import com.bc.web.config.SystemEnum;
import com.bc.web.config.SystemEnum.FilePath;
import com.bc.web.config.SystemEnum.PayOrderItemStatus;
import com.bc.web.model.Merch;
import com.bc.web.model.MerchUser;
import com.bc.web.model.Org;
import com.bc.web.model.PayOrder;
import com.bc.web.model.PayOrderItem;
import com.fs.common.extend.FSController;
import com.fs.common.extend.FSSearchCondition;
import com.fs.common.util.FSDate;
import com.fs.common.util.FSExcel;
import com.fs.common.util.FSHttp;
import com.fs.common.util.FSIDGenerate;
import com.fs.common.util.FSJson;
import com.fs.common.util.FSLog;
import com.fs.common.util.FSMD5;
import com.fs.common.util.FSRegex;
import com.fs.common.util.FSString;
import com.fs.common.util.FSThreadPoolExecutor;
import com.jfinal.aop.Before;
import com.jfinal.aop.Clear;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Page;
import com.jfinal.plugin.activerecord.tx.Tx;
import com.jfinal.upload.UploadFile;

public class PayOrderContoller extends FSController {

	public void search() {
		MerchUser merchUser = (MerchUser) getSession().getAttribute("merchUser");
		FSSearchCondition sc = super.getSearchCondition();
		sc.put("merchCode", merchUser.getMerchCode());
		setPageResultToRequest("items", PayOrder.dao.search(sc));
		render("order/merchPayList.html");
	}

	public void searchItem() {
		String batchNo = getPara("batchNo");
		List<PayOrderItem> items = PayOrderItem.dao.selectByBatchNo(batchNo);
		PayOrder order = PayOrder.dao.selectByBatchNo(batchNo);
		setAttr("order", order);
		setAttr("items", items);
		render("order/merchPayItemList.html");
	}

	public void addPage() {
		render("order/merchPayAdd.html");
	}

	@Before(Tx.class)
	public void add() throws Exception {
		MerchUser merchUser = (MerchUser) getSession().getAttribute("merchUser");
		UploadFile upload = getFile();
		String payPassword = getPara("payPassword");
		if (FSMD5.codeFor(payPassword).equalsIgnoreCase(merchUser.getPassword()) == false) {
			upload.getFile().delete();
			renderFail("密码不正确!");
		} else {
			List<String[]> data = FSExcel.readXlsx(upload.getFile());
			List<String> errorMsg = batchPay(data, merchUser.getMerchCode());
			upload.getFile().delete();
			if (errorMsg.isEmpty()) {
				renderSuccess();
			} else {
				StringBuffer sb = new StringBuffer();
				for (String msg : errorMsg) {
					sb.append(msg + "</br>");
				}
				renderFail(sb.toString());
			}
		}
	}

	public void merchLookup() {
		MerchUser merchUser = (MerchUser) getSession().getAttribute("merchUser");
		FSSearchCondition sc = super.getSearchCondition();
		sc.put("merchCode", merchUser.getMerchCode());
		Page<Merch> items = Merch.dao.search(sc);
		setPageResultToRequest("items", items);
		render("merch/merchLookup.html");
	}
	
	/** 下载报表 */
	public void download() throws FileNotFoundException, IOException {
		//MerchUser merchUser = (MerchUser) getSession().getAttribute("merchUser");
		String batchNo = getPara("batchNo");

		// 批次
		//PayOrder order = PayOrder.dao.selectByBatchNo(batchNo);
		// 明细
		List<PayOrderItem> items = PayOrderItem.dao.selectByBatchNo(batchNo);
		
		// Workbook wb = new XSSFWorkbook();
		Workbook wb = new HSSFWorkbook();
		Sheet sheet = wb.createSheet("sheet1");
		// 标题行
		Row titleRow = sheet.createRow(0);
		titleRow.createCell(0, CellType.STRING).setCellValue("金额(元)");
		titleRow.createCell(1, CellType.STRING).setCellValue("户名");
		titleRow.createCell(2, CellType.STRING).setCellValue("银行卡号");
		titleRow.createCell(3, CellType.STRING).setCellValue("开户银行");
		titleRow.createCell(4, CellType.STRING).setCellValue("收款方类型");
		titleRow.createCell(5, CellType.STRING).setCellValue("状态");
		titleRow.createCell(6, CellType.STRING).setCellValue("创建时间");
		titleRow.createCell(7, CellType.STRING).setCellValue("响应信息");
		// 数据行
		DecimalFormat df = new DecimalFormat("0.00");
		for (int i = 0; i < items.size(); i++) {
			PayOrderItem item = items.get(i);
			Row row = sheet.createRow(i+1);
			row.createCell(0, CellType.STRING).setCellValue(df.format(item.getAmount()/100.0));
			row.createCell(1, CellType.STRING).setCellValue(item.getAccountName());
			row.createCell(2, CellType.STRING).setCellValue(item.getAccountNumber());
			row.createCell(3, CellType.STRING).setCellValue(item.getBankName());
			row.createCell(4, CellType.STRING).setCellValue(item.getIsCompany()?"企业":"个人");
			if ("CO".equals(item.getOrderStatus())) {
				row.createCell(5, CellType.STRING).setCellValue("待确认");
			} else if ("IP".equals(item.getOrderStatus())) {
				row.createCell(5, CellType.STRING).setCellValue("交易处理中");
			} else if ("TS".equals(item.getOrderStatus())) {
				row.createCell(5, CellType.STRING).setCellValue("交易成功");
			} else if ("TF".equals(item.getOrderStatus())) {
				row.createCell(5, CellType.STRING).setCellValue("交易失败");
			} else {
				row.createCell(5, CellType.STRING).setCellValue(item.getOrderStatus());
			} 
			row.createCell(6, CellType.STRING).setCellValue(item.getCreateDateTime());
			row.createCell(7, CellType.STRING).setCellValue(item.getRespCode()+"-"+item.getRespMsg());
		}
		String tmpPath = SystemEnum.FilePath.ReportTmp.getValue();
		File file  = new File(tmpPath + batchNo + "代付结果.xls");
		try  (OutputStream fileOut = new FileOutputStream(file)) {
	        wb.write(fileOut);
		}
		wb.close();
		renderFile(file);
	}

	/** 下载模板 */
	public void template() {
		renderFile(new File(FilePath.ReportTemplate.getValue() + "商户代付模版.xlsx"));
	}

	@Before(Tx.class)
	public void confirm() {
		String batchNo = getPara("batchNo");
		PayOrder order = PayOrder.dao.selectByBatchNo(batchNo);
		order.setIsConfirm(true);
		order.update();
		List<PayOrderItem> items = PayOrderItem.dao.selectByBatchNo(batchNo);
		send(items);
		renderSuccess("已提交申请,实际交易结果需要等待");
	}

	@Before(Tx.class)
	public void remove() {
		String batchNo = getPara("batchNo");
		PayOrder order = PayOrder.dao.selectByBatchNo(batchNo);
		order.delete();
		PayOrderItem.dao.deleteByBatchNo(batchNo);
		renderSuccess(false);
	}

	@Clear
	public void payNotify() {
		String notifyString = getStringFromRequest();
		FSLog.web.info("---------代付回调----------\r\n" + notifyString);
		JSONObject notifyResp = JSONObject.parseObject(notifyString);
		String respCode = notifyResp.getString("respCode");
		String merchCode = notifyResp.getString("merchCode");
		String merchOrderNo = notifyResp.getString("merchOrderNo");
		PayOrderItem orderItem = PayOrderItem.dao.selectByMerchOrderNo(merchCode, merchOrderNo);
		if (orderItem.getOrderStatus().equals(PayOrderItemStatus.InProcess.getValue())) {
			if (Resp.Success.getCode().equals(respCode)) {
				orderItem.setOrderStatus(PayOrderItemStatus.TradeSuccess.getValue());
			} else if (("01").equals(respCode) || ("11").equals(respCode)) {
				orderItem.setOrderStatus(PayOrderItemStatus.TradeFail.getValue());
			}
			orderItem.setRespCode(respCode);
			orderItem.setRespMsg(notifyResp.getString("respMsg"));
			orderItem.setTradeDateTime(FSDate.getDateTime());
			orderItem.update();
		}
		renderText("SUCCESS");
	}

	private List<String> batchPay(List<String[]> data, String merchCode) {
		List<String> errorMsg = new ArrayList<>();
		Long amount;
		String accountName;
		String accountNo;
		String accountType;
		String bankName;
		String remark;
		String createDateTime = FSDate.getDateTime();
		List<PayOrderItem> saveList = new ArrayList<>();
		Merch merch = Merch.dao.selectByMerchCode(merchCode);
		String excelAmount;
		DecimalFormat df = new DecimalFormat("0.00");
		// 生成统计单
		PayOrder order = new PayOrder();
		order.setBatchNo(getBatchNo());
		order.setTotalAmount(0L);
		order.setMerchCode(merchCode);
		order.setOrgCode(merch.getOrgCode());
		order.setIsConfirm(false);
		order.setCreateDateTime(FSDate.getDateTime());
		// 第一行是标题
		String[] rowData;
		for (int i = 3; i < data.size(); i++) {
			rowData = data.get(i);
			if (StrKit.notBlank(rowData[0]) && getDecimalLength(rowData[0]) > 2) {
				errorMsg.add(i + 1 + "行:金额格式校验不通过,请确保小数点不能大于2位");
				continue;
			}

			excelAmount = df.format(new Double(rowData[0]));
			amount = new BigDecimal(excelAmount).multiply(new BigDecimal(100)).longValue();
			accountName = rowData[1].trim();
			accountNo = rowData[2].trim();
			bankName = rowData[3].trim();
			accountType = rowData[4].trim();
			remark = rowData[8].trim();//开户行号目前没有加字段，保存在备注里面

			if (StrKit.isBlank(accountName)) {
				errorMsg.add(i + 1 + "行:户名非空校验不通过");
				continue;
			}

			if (FSRegex.isPositiveInt(accountNo) == false) {
				errorMsg.add(i + 1 + "行:行银行卡号校验不通过");
				continue;
			}

			if (StrKit.isBlank(accountType)) {
				errorMsg.add(i + 1 + "行:收款方类型校验不通过");
				continue;
			}

			if (StrKit.isBlank(remark)) {
				errorMsg.add(i + 1 + "行:开户行号不能为空");
				continue;
			}

			PayOrderItem item = new PayOrderItem();
			item.setId(FSIDGenerate.getUUID());
			item.setOrgCode(merch.getOrgCode());
			item.setMerchCode(merchCode);
			item.setMerchOrderNo(FSIDGenerate.getDateOrderNo());
			item.setAmount(Long.valueOf(amount));
			item.setAccountName(accountName);
			item.setAccountNumber(accountNo);
			item.setBatchNo(order.getBatchNo());
			item.setOrderStatus(PayOrderItemStatus.Create.getValue());
			item.setCreateDateTime(createDateTime);
			item.setBankName(bankName);
			item.setRemark(remark);

			if ("企业".equals(accountType)) {
				if (StrKit.isBlank(rowData[5])) {
					errorMsg.add(i + 1 + "行:[开户行省份]非空校验不通过");
					continue;
				}
				if (StrKit.isBlank(rowData[6])) {
					errorMsg.add(i + 1 + "行:[开户行城市]非空校验不通过");
					continue;
				}
				if (StrKit.isBlank(rowData[7])) {
					errorMsg.add(i + 1 + "行:[开户行支行行名]非空校验不通过");
					continue;
				}
				item.setIsCompany(true);
				item.setProvince(rowData[5]);
				item.setCity(rowData[6]);
				item.setBranchName(rowData[7]);
			} else {
				if (StrKit.isBlank(rowData[5])) {
					errorMsg.add(i + 1 + "行:[开户行省份]非空校验不通过");
					continue;
				}
				if (StrKit.isBlank(rowData[6])) {
					errorMsg.add(i + 1 + "行:[开户行城市]非空校验不通过");
					continue;
				}
				if (StrKit.isBlank(rowData[7])) {
					errorMsg.add(i + 1 + "行:[开户行支行行名]非空校验不通过");
					continue;
				}
				item.setIsCompany(true);
				item.setProvince(rowData[5]);
				item.setCity(rowData[6]);
				item.setBranchName(rowData[7]);
				item.setIsCompany(false);
			}

			saveList.add(item);
			order.setTotalAmount(order.getTotalAmount().longValue() + item.getAmount().longValue());
		}

		if (errorMsg.isEmpty()) {
			String checkMsg = check(data.get(1), order.getTotalAmount(), saveList.size());
			if (StrKit.notBlank(checkMsg)) {
				errorMsg.add(checkMsg);
			} else {
				order.setTotalNumber(saveList.size());
				order.save();
				Db.batchSave(saveList, 100);
			}
		}
		return errorMsg;
	}

	private void send(List<PayOrderItem> items) {
		FSThreadPoolExecutor.execute(()->{
			for (PayOrderItem item : items) {
				final String localDomain = SystemConfig.config.get("local_domain");
				Org org = Org.dao.selectByOrgCode(item.getOrgCode());
				DFReq req = new DFReq();
				req.setVersion("V.10");
				req.setOrgCode(item.getOrgCode());
				req.setMerchCode(item.getMerchCode());
				req.setMerchOrderNo(item.getMerchOrderNo());
				req.setAmount(String.valueOf(item.getAmount()));
				req.setAccountName(item.getAccountName());
				req.setAccountCardNo(item.getAccountNumber());
				req.setBankName(item.getBankName());
				if (item.getIsCompany()) {
					req.setBranchName(item.getBranchName());
					req.setProvince(item.getProvince());
					req.setCity(item.getCity());
					req.setPayType(PayType.DF_COMPANY);
				} else {
					req.setBranchName(item.getBranchName());
					req.setProvince(item.getProvince());
					req.setCity(item.getCity());
					req.setPayType(PayType.DF_PERSONAL);
				}
				req.setNotifyUrl(localDomain + "/merch/payorder/payNotify");
				req.setRemark(item.getRemark());
				req.setSign(FSMD5.sortSign(req, org.getSafeKey()));
				try {
					String tradeSystemUrl = SystemConfig.config.get("trade_system_url") + "/route";
					String result = FSHttp.postJson(tradeSystemUrl, FSJson.toString(req));
					FSLog.web.info("代付交易返回 ------>" + result);
					BaseResp rsp = JSONObject.parseObject(result, BaseResp.class);
					item.setRespCode(rsp.getRespCode());
					item.setRespMsg(rsp.getRespMsg());
					if (item.getRespCode().equals(Resp.Fail.getCode())) {
						item.setOrderStatus(PayOrderItemStatus.TradeFail.getValue());
					} else {
						item.setOrderStatus(PayOrderItemStatus.InProcess.getValue());
					}
					item.update();
					FSLog.web.info("线程池执行代付操作结果 -> \r\n" + result);
				} catch (Exception e) {
					item.setOrderStatus(PayOrderItemStatus.InProcess.getValue());
					FSLog.web.error("线程池执行代付操作结果 -> 异常, 订单号=" + item.getMerchOrderNo(), e);
				} finally {
					item.update();
				}
			}
		});
	}

	/**
	 * 获取小数点后长度
	 * 
	 * @param src
	 * @return
	 */
	private int getDecimalLength(String src) {
		src = src.substring(src.lastIndexOf(".") + 1);
		return src.length();
	}

	private String getBatchNo() {
		String batchNo = Db.queryFirst("select nextval('batchNo')").toString();
		batchNo = FSString.appendZeroToLeft(batchNo, 4);
		batchNo = "P" + FSDate.getDateNumber() + FSIDGenerate.getIntRandomCode(6) + batchNo;
		return batchNo;
	}

	private String check(String[] rowData, Long totalAmount, Integer total) {
		String res = "";
		Long excelTotalAmount = new BigDecimal(rowData[0]).multiply(new BigDecimal(100)).longValue();
		Integer excelTotal = Double.valueOf(rowData[1]).intValue();
		if (excelTotalAmount.longValue() != totalAmount.longValue()) {
			res += "[批总金额]与系统统计不符合";
		}
		if (excelTotal.intValue() != total.intValue()) {
			res += "\t[批总笔数]与系统统计不符合";
		}
		return res;
	}

}
